using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using TelnetExpect;

namespace TelnetTests
{
    [TestFixture]
    public class StatusReceiveTelnetHandlerTests : TestsBase
    {
        const byte IS = 0;
        const byte SEND = 1;

        TesteeStatusReceiveTelnetHandler dut;

        protected override TelnetStream Create_Testee(System.IO.Stream conn)
        {
            TelnetStream t = base.Create_Testee(conn);
            dut = new TesteeStatusReceiveTelnetHandler();
            t.AddOptionHandler(dut);
            return t;
        }

        [Test]
        public void NoneEnabled()
        {
            byte[] rcv = {
                IAC, WILL, OptionCode.Status,
                //
                IAC, SB, OptionCode.Status,
                    IS,
                    IAC,SE
            };
            byte[] myResponses = {
                IAC, DO, OptionCode.Status,
            };
            DoTest(rcv, ExpectedInputNone, myResponses,
                null, null, new TelnetOption[] { TelnetOption.Status });
            dut.DoAssert(new TelnetOption[] { },
                new TelnetOption[] { });
        }

        [Test]
        public void Some()
        {
            byte[] rcv = {
                IAC, WILL, OptionCode.Status,
                //
                IAC, SB, OptionCode.Status,
                    IS,
                    WILL, 11,
                    DO, 12,
                    WILL, 13,
                    DO, 14,
                    WILL, 15,
                    DO, 16,
                    IAC,SE
            };
            byte[] myResponses = {
                IAC, DO, OptionCode.Status,
            };
            DoTest(rcv, ExpectedInputNone, myResponses,
                null, null, new TelnetOption[] { TelnetOption.Status });
            dut.DoAssert(new TelnetOption[] {
                    (TelnetOption)11, (TelnetOption)13, (TelnetOption)15, 
                },
                new TelnetOption[] { 
                    (TelnetOption)12, (TelnetOption)14, (TelnetOption)16, 
                });
        }

        [Test]
        public void Some_DifferentSentOrder()
        {
            byte[] rcv = {
                IAC, WILL, OptionCode.Status,
                //
                IAC, SB, OptionCode.Status,
                    IS,
                    DO, 16,
                    WILL, 15,
                    WILL, 11,
                    DO, 12,
                    WILL, 13,
                    DO, 14,
                    IAC,SE
            };
            byte[] myResponses = {
                IAC, DO, OptionCode.Status,
            };
            DoTest(rcv, ExpectedInputNone, myResponses,
                null, null, new TelnetOption[] { TelnetOption.Status });
            dut.DoAssert(new TelnetOption[] {
                    (TelnetOption)11, (TelnetOption)13, (TelnetOption)15, 
                },
                new TelnetOption[] { 
                    (TelnetOption)12, (TelnetOption)14, (TelnetOption)16, 
                });
        }

        [Test]
        public void NotForUs_Send()
        {
            byte[] rcv = {
                IAC, SB, OptionCode.Status,
                    SEND,
                    IAC,SE
            };
            byte[] myResponses = {
            };
            DoTest(rcv, ExpectedInputNone, myResponses,
                null, null, null);
        }

        //----
        [Test]
        public void SendRequest_WhenStatusNotNegotiated()
        {
            System.IO.MemoryStream dst = new System.IO.MemoryStream();
            PairStream conn = new PairStream(new System.IO.MemoryStream(), dst);
            TelnetStream t = Create_Testee(conn);
            TesteeStatusReceiveTelnetHandler h = new TesteeStatusReceiveTelnetHandler();
            t.AddOptionHandler(h);
            byte[] myResponses = {
                IAC, SB,
                    OptionCode.Status,
                    SEND,
                    IAC, SE,
            };
            Assert_Buffers_AreEqual(new byte[0], dst, "before");
            //
            try {
                h.RequestStatus();
                Assert.Fail("should have thrown!");
            } catch (InvalidOperationException) {
            }
            Assert_Buffers_AreEqual(new byte[0], dst, "after");
        }

        [Test]
        public void SendRequest()
        {
            byte[] server = {
                IAC, WILL, OptionCode.Status,
                //
                (byte)'a',
            };
            System.IO.MemoryStream src = new System.IO.MemoryStream(server, false);
            System.IO.MemoryStream dst = new System.IO.MemoryStream();
            PairStream conn = new PairStream(src, dst);
            TelnetStream t = Create_Testee(conn);
            TesteeStatusReceiveTelnetHandler h = new TesteeStatusReceiveTelnetHandler();
            t.AddOptionHandler(h);
            //
            byte[] forceReadResponseNego = new byte[1];
            int readLen = t.Read(forceReadResponseNego, 0, forceReadResponseNego.Length);
            Assert.AreEqual(1, readLen, "hack");
            //
            byte[] myResponses0 = {
                IAC, DO, OptionCode.Status,
            };
            byte[] myResponses1 = {
                IAC, DO, OptionCode.Status,
                //
                IAC, SB,
                    OptionCode.Status,
                    SEND,
                    IAC, SE,
            };
            Assert_Buffers_AreEqual(myResponses0, dst, "before");
            //
            h.RequestStatus(); //****
            Assert_Buffers_AreEqual(myResponses1, dst, "after");
        }


        //----
        class TesteeStatusReceiveTelnetHandler : StatusReceiveTelnetHandler
        {
            StringBuilder log = new StringBuilder();

            protected override void OnReceivedStatus(TelnetOption[] myRemoteOptions, TelnetOption[] yourLocalOptions)
            {
                log.AppendFormat("Received Status: my/remote: [ {0} ]; yours/local: [ {1} ].",
                    Join(myRemoteOptions), Join(yourLocalOptions));
                log.AppendLine();
            }

            internal static string Join<T>(IEnumerable<T> vars)
            {
                const string Sepa = ", ";
                StringBuilder bldr = new StringBuilder();
                foreach (T cur in vars) {
                    bldr.Append(cur);
                    bldr.Append(Sepa);
                }
                if (bldr.Length > Sepa.Length) {
                    bldr.Length -= Sepa.Length;
                }
                return bldr.ToString();
            }

            internal void DoAssert(TelnetOption[] expectedRemote, TelnetOption[] expectedLocal)
            {
                StringBuilder expected = new StringBuilder();
                expected.AppendFormat("Received Status: my/remote: [ {0} ]; yours/local: [ {1} ].",
                    Join(expectedRemote), Join(expectedLocal));
                expected.AppendLine();
                Assert.AreEqual(expected.ToString(), log.ToString());
            }
        }

    }
}
